package com.starview.star.compiler;

import java.util.ArrayList;
import java.util.List;

import com.starview.platform.data.type.Location;
import com.starview.star.compiler.util.PrettyPrintDisplay;
import com.starview.star.compiler.util.PrettyPrintable;

/**
 * An ErrorReport holds a record of what errors have occurred and where
 * 
 * @author fgm
 * 
 */
@SuppressWarnings("serial")
public class ErrorReport implements PrettyPrintable
{
  private final List<CompilerMessage> errors = new ArrayList<>();
  private final List<CompilerMessage> warnings = new ArrayList<>();
  private final List<CompilerMessage> infos = new ArrayList<>();

  /**
   * Report a compilation error.
   * 
   * @param msg
   *          The string that should be displayed to the user
   * @param locs
   *          Where in the source the error was detected. There may be more than one location.
   */
  public void reportError(String msg, Location... locs)
  {
    CompilerMessage error = new CompilerMessage(msg, locs);
    if (!errors.contains(error)) {
      errors.add(error);
    }
  }

  /**
   * Report a compiler warning.
   * 
   * @param msg
   *          The string that should be displayed to the user
   * @param locs
   *          Where in the source the warning was detected
   */
  public void reportWarning(String msg, Location... locs)
  {
    CompilerMessage warning = new CompilerMessage(msg, locs);
    if (!warnings.contains(warning))
      warnings.add(warning);
  }

  /**
   * Report some 'information'.
   * 
   * @param msg
   *          The string that should be displayed to the user
   * @param locs
   *          Where in the source the warning was detected
   */
  public void reportInfo(String msg, Location... locs)
  {
    CompilerMessage info = new CompilerMessage(msg, locs);
    if (!infos.contains(info))
      infos.add(info);
  }

  public void mergeReport(ErrorReport other)
  {
    for (CompilerMessage error : other.errors)
      if (!errors.contains(error))
        errors.add(error);
    for (CompilerMessage warn : other.warnings)
      if (!warnings.contains(warn))
        warnings.add(warn);
    for (CompilerMessage info : other.infos)
      if (!infos.contains(info))
        infos.add(info);
  }

  public int errorCount()
  {
    return errors.size();
  }

  public boolean noNewErrors(int count)
  {
    return errors.size() == count;
  }

  /**
   * Get a list of the errors that have been reported
   * 
   * @return list of the errors
   */
  public List<CompilerMessage> getErrors()
  {
    return errors;
  }

  /**
   * Get a list of the warnings that have been reported
   * 
   * @return list of the warnings
   */
  public List<CompilerMessage> getWarnings()
  {
    return warnings;
  }

  public List<CompilerMessage> getInfos()
  {
    return infos;
  }

  /**
   * Occasionally we need to gate a later phase by ensuring that there are no errors so far.
   * 
   * @return true if there are no errors
   */
  public boolean isErrorFree()
  {
    return errors.isEmpty();
  }

  public boolean isWarningAndErrorFree()
  {
    return warnings.isEmpty() && errors.isEmpty();
  }

  @Override
  public void prettyPrint(PrettyPrintDisplay disp)
  {
    String sep = "";
    int mark = disp.markIndent();
    for (CompilerMessage msg : errors) {
      disp.append(sep);
      disp.append("Error: ");
      msg.prettyPrint(disp);
      sep = "\n";
    }

    for (CompilerMessage msg : warnings) {
      disp.append(sep);
      disp.append("Warning: ");
      msg.prettyPrint(disp);
      sep = "\n";
    }

    for (CompilerMessage msg : infos) {
      disp.append(sep);
      disp.append("Info: ");
      msg.prettyPrint(disp);
      sep = "\n";
    }

    disp.popIndent(mark);
  }

  @Override
  public String toString()
  {
    return PrettyPrintDisplay.toString(this);
  }

  public void clear()
  {
    infos.clear();
    warnings.clear();
    errors.clear();
  }

  public static class NullErrorReporter extends ErrorReport{
    @Override
    public void reportError(String msg, Location... locs){
    }

    @Override
    public void reportWarning(String msg, Location... locs){
    }

    @Override
    public void reportInfo(String msg, Location... locs){
    }
  }

}
